#include "../stdafx.h"
#include "../include/Platform.h"
#include "Selection.h"

namespace soy{

	void SelectionPosition::MoveForInsertDelete(bool insertion, int startChange, int length) {
		if (position == startChange) {
			virtualSpace = 0;
		}
		if (insertion) {
			if (position > startChange) {
				position += length;
			}
		} else {
			if (position > startChange) {
				int endDeletion = startChange + length;
				if (position > endDeletion) {
					position -= length;
				} else {
					position = startChange;
					virtualSpace = 0;
				}
			}
		}
	}

	bool SelectionPosition::operator <(const SelectionPosition &other) const {
		if (position == other.position)
			return virtualSpace < other.virtualSpace;
		else
			return position < other.position;
	}

	bool SelectionPosition::operator >(const SelectionPosition &other) const {
		if (position == other.position)
			return virtualSpace > other.virtualSpace;
		else
			return position > other.position;
	}

	bool SelectionPosition::operator <=(const SelectionPosition &other) const {
		if (position == other.position && virtualSpace == other.virtualSpace)
			return true;
		else
			return other > *this;
	}

	bool SelectionPosition::operator >=(const SelectionPosition &other) const {
		if (position == other.position && virtualSpace == other.virtualSpace)
			return true;
		else
			return *this > other;
	}

	int SelectionRange::Length() const {
		if (anchor > caret) {
			return anchor.Position() - caret.Position();
		} else {
			return caret.Position() - anchor.Position();
		}
	}

	bool SelectionRange::Contains(int pos) const {
		if (anchor > caret)
			return (pos >= caret.Position()) && (pos <= anchor.Position());
		else
			return (pos >= anchor.Position()) && (pos <= caret.Position());
	}

	bool SelectionRange::Contains(SelectionPosition sp) const {
		if (anchor > caret)
			return (sp >= caret) && (sp <= anchor);
		else
			return (sp >= anchor) && (sp <= caret);
	}

	bool SelectionRange::ContainsCharacter(int posCharacter) const {
		if (anchor > caret)
			return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());
		else
			return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
	}

	SelectionSegment SelectionRange::Intersect(SelectionSegment check) const {
		SelectionSegment inOrder(caret, anchor);
		if ((inOrder.start <= check.end) || (inOrder.end >= check.start)) {
			SelectionSegment portion = check;
			if (portion.start < inOrder.start)
				portion.start = inOrder.start;
			if (portion.end > inOrder.end)
				portion.end = inOrder.end;
			if (portion.start > portion.end)
				return SelectionSegment();
			else
				return portion;
		} else {
			return SelectionSegment();
		}
	}

	bool SelectionRange::Trim(SelectionRange range) {
		SelectionPosition startRange = range.Start();
		SelectionPosition endRange = range.End();
		SelectionPosition start = Start();
		SelectionPosition end = End();
		assert(start <= end);
		assert(startRange <= endRange);
		if ((startRange <= end) && (endRange >= start)) {
			if ((start > startRange) && (end < endRange)) {
				// Completely covered by range -> empty at start
				end = start;
			} else if ((start < startRange) && (end > endRange)) {
				// Completely covers range -> empty at start
				end = start;
			} else if (start <= startRange) {
				// Trim end
				end = startRange;
			} else { //
				assert(end >= endRange);
				// Trim start
				start = endRange;
			}
			if (anchor > caret) {
				caret = start;
				anchor = end;
			} else {
				anchor = start;
				caret = end;
			}
			return Empty();
		} else {
			return false;
		}
	}

	// If range is all virtual collapse to start of virtual space
	void SelectionRange::MinimizeVirtualSpace() {
		if (caret.Position() == anchor.Position()) {
			int virtualSpace = caret.VirtualSpace();
			if (virtualSpace > anchor.VirtualSpace())
				virtualSpace = anchor.VirtualSpace();
			caret.SetVirtualSpace(virtualSpace);
			anchor.SetVirtualSpace(virtualSpace);
		}
	}

	Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream) {
		AddSelection(SelectionPosition(0));
	}

	Selection::~Selection() {
	}

	bool Selection::IsRectangular() const {
		return (selType == selRectangle) || (selType == selThin);
	}

	int Selection::MainCaret() const {
		return ranges[mainRange].caret.Position();
	}

	int Selection::MainAnchor() const {
		return ranges[mainRange].anchor.Position();
	}

	SelectionRange &Selection::Rectangular() {
		return rangeRectangular;
	}

	SelectionSegment Selection::Limits() const {
		if (ranges.empty()) {
			return SelectionSegment();
		} else {
			SelectionSegment sr(ranges[0].anchor, ranges[0].caret);
			for (size_t i=1; i<ranges.size(); i++) {
				sr.Extend(ranges[i].anchor);
				sr.Extend(ranges[i].caret);
			}
			return sr;
		}
	}

	SelectionSegment Selection::LimitsForRectangularElseMain() const {
		if (IsRectangular()) {
			return Limits();
		} else {
			return SelectionSegment(ranges[mainRange].caret, ranges[mainRange].anchor);
		}
	}

	size_t Selection::Count() const {
		return ranges.size();
	}

	size_t Selection::Main() const {
		return mainRange;
	}

	void Selection::SetMain(size_t r) {
		assert(r < ranges.size());
		mainRange = r;
	}

	SelectionRange &Selection::Range(size_t r) {
		return ranges[r];
	}

	SelectionRange &Selection::RangeMain() {
		return ranges[mainRange];
	}

	bool Selection::MoveExtends() const {
		return moveExtends;
	}

	void Selection::SetMoveExtends(bool moveExtends_) {
		moveExtends = moveExtends_;
	}

	bool Selection::Empty() const {
		for (size_t i=0; i<ranges.size(); i++) {
			if (!ranges[i].Empty())
				return false;
		}
		return true;
	}

	SelectionPosition Selection::Last() const {
		SelectionPosition lastPosition;
		for (size_t i=0; i<ranges.size(); i++) {
			if (lastPosition < ranges[i].caret)
				lastPosition = ranges[i].caret;
			if (lastPosition < ranges[i].anchor)
				lastPosition = ranges[i].anchor;
		}
		return lastPosition;
	}

	int Selection::Length() const {
		int len = 0;
		for (size_t i=0; i<ranges.size(); i++) {
			len += ranges[i].Length();
		}
		return len;
	}

	void Selection::MovePositions(bool insertion, int startChange, int length) {
		for (size_t i=0; i<ranges.size(); i++) {
			ranges[i].caret.MoveForInsertDelete(insertion, startChange, length);
			ranges[i].anchor.MoveForInsertDelete(insertion, startChange, length);
		}
	}

	void Selection::TrimSelection(SelectionRange range) {
		for (size_t i=0; i<ranges.size();) {
			if ((i != mainRange) && (ranges[i].Trim(range))) {
				// Trimmed to empty so remove
				for (size_t j=i; j<ranges.size()-1; j++) {
					ranges[j] = ranges[j+1];
					if (j == mainRange-1)
						mainRange--;
				}
				ranges.pop_back();
			} else {
				i++;
			}
		}
	}

	void Selection::SetSelection(SelectionRange range) {
		ranges.clear();
		ranges.push_back(range);
		mainRange = ranges.size() - 1;
	}

	void Selection::AddSelection(SelectionRange range) {
		TrimSelection(range);
		ranges.push_back(range);
		mainRange = ranges.size() - 1;
	}

	void Selection::AddSelectionWithoutTrim(SelectionRange range) {
		ranges.push_back(range);
		mainRange = ranges.size() - 1;
	}

	void Selection::TentativeSelection(SelectionRange range) {
		if (!tentativeMain) {
			rangesSaved = ranges;
		}
		ranges = rangesSaved;
		AddSelection(range);
		TrimSelection(ranges[mainRange]);
		tentativeMain = true;
	}

	void Selection::CommitTentative() {
		rangesSaved.clear();
		tentativeMain = false;
	}

	int Selection::CharacterInSelection(int posCharacter) const {
		for (size_t i=0; i<ranges.size(); i++) {
			if (ranges[i].ContainsCharacter(posCharacter))
				return i == mainRange ? 1 : 2;
		}
		return 0;
	}

	int Selection::InSelectionForEOL(int pos) const {
		for (size_t i=0; i<ranges.size(); i++) {
			if (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position()))
				return i == mainRange ? 1 : 2;
		}
		return 0;
	}

	int Selection::VirtualSpaceFor(int pos) const {
		int virtualSpace = 0;
		for (size_t i=0; i<ranges.size(); i++) {
			if ((ranges[i].caret.Position() == pos) && (virtualSpace < ranges[i].caret.VirtualSpace()))
				virtualSpace = ranges[i].caret.VirtualSpace();
			if ((ranges[i].anchor.Position() == pos) && (virtualSpace < ranges[i].anchor.VirtualSpace()))
				virtualSpace = ranges[i].anchor.VirtualSpace();
		}
		return virtualSpace;
	}

	void Selection::Clear() {
		ranges.clear();
		ranges.push_back(SelectionRange());
		mainRange = ranges.size() - 1;
		selType = selStream;
		moveExtends = false;
		ranges[mainRange].Reset();
		rangeRectangular.Reset();
	}

	void Selection::RemoveDuplicates() {
		for (size_t i=0; i<ranges.size()-1; i++) {
			if (ranges[i].Empty()) {
				size_t j=i+1;
				while (j<ranges.size()) {
					if (ranges[i] == ranges[j]) {
						ranges.erase(ranges.begin() + j);
						if (mainRange >= j)
							mainRange--;
					} else {
						j++;
					}
				}
			}
		}
	}

	void Selection::RotateMain() {
		mainRange = (mainRange + 1) % ranges.size();
	}



};
